package org.example.utils;

import io.minio.*;
import io.minio.errors.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.example.properties.minio.minioProperties;
import org.jetbrains.annotations.NotNull;
import org.springframework.http.MediaType;
import org.springframework.http.MediaTypeFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;

@Slf4j
@Component
@Data
@NoArgsConstructor
@AllArgsConstructor

public class minioUtils {
    private minioProperties minioProperties;


    /***
     * 删除minio中的文件
     *
     * @param objectName 文件名称
     * return 是否删除成功
     */
    public boolean deleteFile(@NotNull String objectName) {
        try {
            getPropertiesMinioClient().removeObject(RemoveObjectArgs.builder()
                    // 存储桶名称
                    .bucket(minioProperties.getBucketName())
                    // 文件名称
                    .object(objectName).build()
            );
            return true;
        } catch (Exception e) {
            log.error("删除文件失败", e);
            return false;
        }
    }

    /***
     * 获取resource下的文件上传到minio
     * @param filePathName 项目中 resource（src/main/resources）下的文件路径名称
     * @return 上传后文件的访问地址作为字符串
     * @throws IOException               在处理文件时发生的 I/O 错误
     * @throws ServerException           在通过 MinIO 客户端进行通信时发生的服务器端异常
     * @throws InsufficientDataException 在上传过程中数据不足时抛出的异常
     * @throws ErrorResponseException    响应错误时抛出的异常
     * @throws NoSuchAlgorithmException  当使用的加密算法不存在时抛出的异常
     * @throws InvalidKeyException       密钥无效时抛出的异常
     * @throws InvalidResponseException  响应无效时抛出的异常
     * @throws XmlParserException        XML 解析错误时抛出的异常
     * @throws InternalException         内部错误时抛出的异常
     */
    public String uploadResourcesFile(String filePathName) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        URL resource = this.getClass().getClassLoader().getResource(filePathName);
        String path = URLDecoder.decode(Objects.requireNonNull(resource).getFile(), StandardCharsets.UTF_8);
        return uploadFile(path);
    }

    public String uploadFile(String filePath) throws IOException, ServerException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        return uploadFile(new File(filePath));
    }

    /**
     * 将指定的文件上传到 MinIO 对象存储中，并返回上传后文件的访问地址
     *
     * @param filePath 文件的路径，作为 File 对象
     * @return 上传后文件的访问地址作为字符串
     * @throws IOException               在处理文件时发生的 I/O 错误
     * @throws ServerException           在通过 MinIO 客户端进行通信时发生的服务器端异常
     * @throws InsufficientDataException 在上传过程中数据不足时抛出的异常
     * @throws ErrorResponseException    响应错误时抛出的异常
     * @throws NoSuchAlgorithmException  当使用的加密算法不存在时抛出的异常
     * @throws InvalidKeyException       密钥无效时抛出的异常
     * @throws InvalidResponseException  响应无效时抛出的异常
     * @throws XmlParserException        XML 解析错误时抛出的异常
     * @throws InternalException         内部错误时抛出的异常
     */
    public String uploadFile(File filePath) throws IOException, ServerException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        String uuidObjectName = new SimpleDateFormat("yyyy-MM-dd").format(new Date())
                + "/" + UUID.randomUUID() + "&" + filePath.getName();
        // 获取文件类型
        Optional<MediaType> mediaType = MediaTypeFactory.getMediaType(filePath.getName());
        String contentType = mediaType.orElse(MediaType.APPLICATION_OCTET_STREAM).toString();
        log.info("File类上传文件类型：{}", contentType);

        return uploadFile(uuidObjectName, contentType, filePath.getAbsolutePath());
    }

    /**
     * 将 MultipartFile 文件上传到 MinIO 对象存储，并返回文件的访问 URL
     *
     * @param file 要上传的文件，作为 MultipartFile 对象
     * @return 上传后文件的访问 URL，如果上传成功
     * @throws IOException               在处理文件时发生的 I/O 错误
     * @throws ServerException           在通过 MinIO 客户端进行通信时发生的服务器端异常
     * @throws InsufficientDataException 在上传过程中数据不足时抛出的异常
     * @throws ErrorResponseException    响应错误时抛出的异常
     * @throws NoSuchAlgorithmException  当使用的加密算法不存在时抛出的异常
     * @throws InvalidKeyException       密钥无效时抛出的异常
     * @throws InvalidResponseException  响应无效时抛出的异常
     * @throws XmlParserException        XML 解析错误时抛出的异常
     * @throws InternalException         内部错误时抛出的异常
     */
    public String uploadFile(MultipartFile file) throws IOException, ServerException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        // 获取文件类型
        String contentType = file.getContentType();
        log.info("MultipartFile类上传文件类型：{}", contentType);
        String uuidObjectName = new SimpleDateFormat("yyyy-MM-dd").format(new Date())
                + "/" + UUID.randomUUID() + "&" + file.getOriginalFilename();
        log.info("上传文件名称：{}", uuidObjectName);

        return uploadFile(uuidObjectName, contentType, file);
    }

    /**
     * 上传文件到 MinIO 存储服务器
     *
     * @param inputStream 文件输入流
     * @param fileName    文件名
     * @return 上传后文件的访问 URL，如果上传成功
     * @throws IOException               在处理文件时发生的 I/O 错误
     * @throws ServerException           在通过 MinIO 客户端进行通信时发生的服务器端异常
     * @throws InsufficientDataException 在上传过程中数据不足时抛出的异常
     * @throws ErrorResponseException    响应错误时抛出的异常
     * @throws NoSuchAlgorithmException  当使用的加密算法不存在时抛出的异常
     * @throws InvalidKeyException       密钥无效时抛出的异常
     * @throws InvalidResponseException  响应无效时抛出的异常
     * @throws XmlParserException        XML 解析错误时抛出的异常
     * @throws InternalException         内部错误时抛出的异常
     */
    public String uploadFile(InputStream inputStream, String fileName) throws IOException, ServerException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {

        Optional<MediaType> mediaType = MediaTypeFactory.getMediaType(fileName);
        String contentType = mediaType.orElse(MediaType.APPLICATION_OCTET_STREAM).toString();
        log.info("InputStream类上传文件类型：{}", contentType);
        // 上传文件
        MinioClient minioClient = getMinioClient(minioProperties.getBucketName(), minioProperties.getEndpoint(), minioProperties.getAccessKey(), minioProperties.getSecretKey());
        String uuidObjectName = new SimpleDateFormat("yyyy-MM-dd").format(new Date())
                + "/" + UUID.randomUUID() + "&" + fileName;
        extracted(uuidObjectName, contentType, inputStream, inputStream.available(), minioClient);
        return minioProperties.getEndpoint() + "/" + minioProperties.getBucketName()
                + "/" + uuidObjectName;
    }

    /**
     * 将 MultipartFile 文件上传到 MinIO 对象存储，并返回文件的访问 URL
     *
     * @param filename 要上传的文件，作为 MultipartFile 对象
     * @return 上传后文件的访问 URL，如果上传成功
     * @throws IOException               在处理文件时发生的 I/O 错误
     * @throws ServerException           在通过 MinIO 客户端进行通信时发生的服务器端异常
     * @throws InsufficientDataException 在上传过程中数据不足时抛出的异常
     * @throws ErrorResponseException    响应错误时抛出的异常
     * @throws NoSuchAlgorithmException  当使用的加密算法不存在时抛出的异常
     * @throws InvalidKeyException       密钥无效时抛出的异常
     * @throws InvalidResponseException  响应无效时抛出的异常
     * @throws XmlParserException        XML 解析错误时抛出的异常
     * @throws InternalException         内部错误时抛出的异常
     */
    public InputStream getFile(String filename) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        MinioClient minioClient = getPropertiesMinioClient();
        // 获取文件流
        return minioClient.getObject(GetObjectArgs.builder()
                .bucket(minioProperties.getBucketName()) // 替换为你实际的存储桶名称
                .object(filename)
                .build());
    }


    private String uploadFile(String fileName, String contentType, MultipartFile file) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        MinioClient minioClient = getPropertiesMinioClient();

        extracted(fileName, contentType, file.getInputStream(), file.getSize(), minioClient);
        return minioProperties.getEndpoint() + "/" + minioProperties.getBucketName()
                + "/" + fileName;
    }

    private void extracted(String fileName, String contentType, InputStream inputStream, long size, MinioClient minioClient) throws ErrorResponseException, InsufficientDataException, InternalException, InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, ServerException, XmlParserException {
        PutObjectArgs build = PutObjectArgs.builder()
                .bucket(minioProperties.getBucketName())
                .object(fileName)
                .stream(inputStream, size, -1)
                .contentType(contentType)
                .build();
        minioClient.putObject(build);
    }

    public String uploadFile(String fileName, String contentType, String filePath) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        MinioClient minioClient = getPropertiesMinioClient();
        UploadObjectArgs build = UploadObjectArgs.builder()
                .bucket(minioProperties.getBucketName())
                .object(fileName)
                .filename(filePath)
                .contentType(contentType)
                .build();

        minioClient.uploadObject(build);
        return minioProperties.getEndpoint() + "/" + minioProperties.getBucketName()
                + "/" + fileName;
    }

    private MinioClient getPropertiesMinioClient() throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        return getMinioClient(minioProperties.getBucketName(), minioProperties.getEndpoint(), minioProperties.getAccessKey(), minioProperties.getSecretKey());
    }


    private @NotNull MinioClient getMinioClient(String bucketName, String endpoint, String accessKey, String secretKey) throws ErrorResponseException, InsufficientDataException, InternalException, InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, ServerException, XmlParserException {
        MinioClient minioClient = MinioClient.builder().endpoint(endpoint).credentials(accessKey, secretKey).build();
        boolean bucketExists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        if (!bucketExists) {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
        }
        return minioClient;
    }


}

